-- 
-- Please see the readme.txt file included with this distribution for 
-- attribution and copyright information.
--

function message(sMsg, nodeCTEntry, gmflag, target)
	-- ADD NAME OF CT ENTRY TO NOTIFICATION
	if nodeCTEntry then
		sMsg = sMsg .. " [on " .. NodeManager.get(nodeCTEntry, "name", "") .. "]";
	end

	-- BUILD MESSAGE OBJECT
	local msg = {font = "msgfont", icon = "indicator_effect", text = sMsg};
	
	-- DELIVER MESSAGE BASED ON TARGET AND GMFLAG
	if target then
		if msguser == "" then
			ChatManager.addMessage(msg);
		else
			ChatManager.deliverMessage(msg, msguser);
		end
	elseif gmflag then
		msg.text = "[GM] " .. msg.text;
		if User.isHost() then
			ChatManager.addMessage(msg);
		else
			ChatManager.deliverMessage(msg, User.getUsername());
		end
	else
		ChatManager.deliverMessage(msg);
	end
end

function parseEffect(s)
	local effectlist = {};
	
	local eff_clause;
	for eff_clause in string.gmatch(s, "([^;]*);?") do
		local words = StringManager.parseWords(eff_clause, "%[%]");
		if #words > 0 then
			local eff_type = string.match(words[1], "^([^:]+):");
			local eff_dice = {};
			local eff_mod = 0;
			local eff_remainder = {};
			local rem_index = 1;
			if eff_type then
				rem_index = 2;

				local valcheckstr = "";
				local type_remainder = string.sub(words[1], #eff_type + 2);
				if type_remainder == "" then
					valcheckstr = words[2] or "";
					rem_index = rem_index + 1;
				else
					valcheckstr = type_remainder;
				end
				
				if StringManager.isDiceString(valcheckstr) then
					eff_dice, eff_mod = StringManager.convertStringToDice(valcheckstr);
				elseif valcheckstr ~= "" then
					table.insert(eff_remainder, valcheckstr);
				end
			end
			
			for i = rem_index, #words do
				table.insert(eff_remainder, words[i]);
			end

			table.insert(effectlist, {type = eff_type or "", mod = eff_mod, dice = eff_dice, remainder = eff_remainder});
		end
	end
	
	return effectlist;
end

function rebuildParsedEffect(aEffectComps)
	local aEffect = {};
	
	for keyComp, rComp in ipairs(aEffectComps) do
		local aComp = {};
		if rComp.type ~= "" then
			table.insert(aComp, rComp.type .. ":");
		end

		local sDiceString = StringManager.convertDiceToString(rComp.dice, rComp.mod);
		if sDiceString ~= "" then
			table.insert(aComp, sDiceString);
		end
		
		for keyRemainder, sRemainder in ipairs(rComp.remainder) do
			table.insert(aComp, sRemainder);
		end
		
		table.insert(aEffect, table.concat(aComp, " "));
	end
	
	return table.concat(aEffect, "; ");
end

function getEffectsString(node_ctentry)
	-- Make sure we can get to the effects list
	local node_list_effects = NodeManager.createChild(node_ctentry, "effects");
	if not node_list_effects then
		return "";
	end

	-- Start with an empty effects list string
	local s = "";
	
	-- Iterate through each effect
	for k,v in pairs(node_list_effects.getChildren()) do
		if NodeManager.get(v, "isactive", 0) ~= 0 then
			local sLabel = NodeManager.get(v, "label", "");

			local bAddEffect = true;
			local bGMOnly = false;
			if sLabel == "" then
				bAddEffect = false;
			elseif NodeManager.get(v, "isgmonly", 0) == 1 then
				if User.isHost() then
					bGMOnly = true;
				else
					bAddEffect = false;
				end
			end

			if bAddEffect then
				if isTargetedEffect(v) then
					local sTargets = "TRGT*" .. table.concat(getEffectTargets(v, true), ",") .. "*;";
					if string.match(sLabel, "TRGT;") then
						sLabel = string.gsub(sLabel, "TRGT;", sTargets, 1);
					else
						sLabel = sTargets .. " " .. sLabel;
					end
				end
				local sApply = NodeManager.get(v, "apply", "");
				if sApply == "once" then
					sLabel = "ONCE; " .. sLabel;
				elseif sApply == "single" then
					sLabel = "SINGLE; " .. sLabel;
				end
				if bGMOnly then
					sLabel = "(" .. sLabel .. ")";
				end

				if s ~= "" then
					s = s .. " | ";
				end
				s = s .. sLabel;
			end
		end
	end
	
	-- Return the final effect list string
	return s;
end

function isGMEffect(nodeActor, nodeEffect)
	local bGMOnly = false;
	if nodeEffect then
		bGMOnly = (NodeManager.get(nodeEffect, "isgmonly", 0) == 1);
	end
	if nodeActor then
		if (NodeManager.get(nodeActor, "type", "") ~= "pc") and 
				(NodeManager.get(nodeActor, "show_npc", 0) == 0) then
			bGMOnly = true;
		end
	end
	return bGMOnly;
end

function isTargetedEffect(nodeEffect)
	local bTargeted = false;

	if nodeEffect then
		local nodeTargetList = nodeEffect.getChild("targets");
		if nodeTargetList then
			if nodeTargetList.getChildCount() > 0 then
				bTargeted = true;
			end
		end
	end

	return bTargeted;	
end

function getEffectTargets(nodeEffect, bUseName)
	local aTargets = {};
	
	if nodeEffect then
		local nodeTargetList = nodeEffect.getChild("targets");
		if nodeTargetList then
			for keyTarget, nodeTarget in pairs(nodeTargetList.getChildren()) do
				local sNode = NodeManager.get(nodeTarget, "noderef", "");
				if bUseName then
					local nodeTargetCT = DB.findNode(sNode);
					table.insert(aTargets, NodeManager.get(nodeTargetCT, "name", ""));
				else
					table.insert(aTargets, sNode);
				end
			end
		end
	end

	return aTargets;
end

function removeEffect(nodeCTEntry, sEffPatternToRemove)
	-- VALIDATE
	if not nodeCTEntry or not sEffPatternToRemove then
		return;
	end

	-- GET EFFECTS LIST
	local nodeEffectsList = NodeManager.createChild(nodeCTEntry, "effects");
	if not nodeEffectsList then
		return;
	end

	-- COMPARE EFFECT NAMES TO EFFECT TO REMOVE
	for keyEffect, nodeEffect in pairs(nodeEffectsList.getChildren()) do
		local s = NodeManager.get(nodeEffect, "label", "");
		if string.match(s, sEffPatternToRemove) then
			nodeEffect.delete();
			return;
		end
	end
end

function addEffect(sUser, sIdentity, nodeCT, rNewEffect, sEffectTargetNode)
	-- VALIDATE
	if not nodeCT or not rNewEffect or not rNewEffect.sName then
		return;
	end
	rNewEffect.sExpire = rNewEffect.sExpire or "";
	rNewEffect.nSaveMod = rNewEffect.nSaveMod or 0;
	rNewEffect.nInit = rNewEffect.nInit or 0;
	
	-- GET EFFECTS LIST
	local sCTName = NodeManager.get(nodeCT, "name", "");
	local nodeEffectsList = NodeManager.createChild(nodeCT, "effects");
	if not nodeEffectsList then
		return;
	end
	
	-- TRACK ALTERNATE EFFECT APPLICATION MESSAGES
	local sMsg = "";
	
	-- PARSE NEW EFFECT
	local aNewEffectParse = parseEffect(rNewEffect.sName);
	
	-- USE DEFAULT EXPIRATION FOR SINGLE/SINGLES OR ZONE/AURA IF NO EXPIRATION DEFINED
	local nActiveInit = CombatCommon.getActiveInit();
	if nActiveInit then
		if rNewEffect.sExpire == "" then
			for keyEffComp, rEffComp in pairs(aNewEffectParse) do
				if rEffComp.type == "" then
					local bBreak = false;
					for keyRemainder, sRemainder in pairs(rEffComp.remainder) do
						if StringManager.isWord(sRemainder, {"ZONE", "AURA"}) then
							rNewEffect.sExpire = "start";
							rNewEffect.nInit = nActiveInit;
							break;
						end
					end
				end
			end
		end
		if rNewEffect.sExpire == "" and StringManager.isWord(rNewEffect.sApply, {"once", "single"}) then
			rNewEffect.sExpire = "end";
			rNewEffect.nInit = nActiveInit;
		end
	end
	
	-- CHECKS TO IGNORE NEW EFFECT (DUPLICATE, SHORTER, WEAKER)
	local nodeTargetEffect = nil;
	for k, v in pairs(nodeEffectsList.getChildren()) do
		-- PARSE EFFECT FROM LIST
		local sEffectName = NodeManager.get(v, "label", "");
		local nEffectSaveMod = NodeManager.get(v, "savemod", 0);
		local sEffectExpire = NodeManager.get(v, "expiration", "");
		local nEffectInit = NodeManager.get(v, "effectinit", 0);

		local aEffectParse = parseEffect(sEffectName);

		-- CHECK FOR DUPLICATE OR SHORTER EFFECT
		-- NOTE: ONLY IF LABEL AND MODIFIER ARE THE SAME
		if sEffectName == rNewEffect.sName then
			-- COMPARE EXPIRATIONS
			local bLonger = false;
			if rNewEffect.sExpire == "" then
				if sEffectExpire ~= "" then
					bLonger = true;
				end
			elseif rNewEffect.sExpire == "save" then
				if StringManager.isWord(sEffectExpire, {"encounter", "endnext", "start", "end"}) then
					bLonger = true;
				end
			elseif rNewEffect.sExpire == "encounter" then
				if StringManager.isWord(sEffectExpire, {"endnext", "start", "end"}) then
					bLonger = true;
				end
			elseif rNewEffect.sExpire == "endnext" then
				if StringManager.isWord(sEffectExpire, {"start", "end"}) then
					bLonger = true;
				elseif sEffectExpire == "endnext" then
					local nCurrentInit = NodeManager.get(CombatCommon.getActiveCT(), "initresult", 10000);
					if ((nEffectInit - nCurrentInit) * (rNewEffect.nInit - nCurrentInit) < 0) then
						if rNewEffect.nInit > nEffectInit then
							bLonger = true;
						end
					else
						if rNewEffect.nInit < nEffectInit then
							bLonger = true;
						end
					end
				end
			elseif StringManager.isWord(rNewEffect.sExpire, {"start", "end"}) then
				if StringManager.isWord(sEffectExpire, {"start", "end"}) then
					local nCurrentInit = NodeManager.get(CombatCommon.getActiveCT(), "initresult", 10000);
					local nCurrentValue = nCurrentInit * 3;

					local nEffectValue = nEffectInit * 3;
					if sEffectExpire == "start" then
						nEffectValue = nEffectValue + 1;
					elseif sEffectExpire == "end" then
						nEffectValue = nEffectValue - 1;
					end

					local nNewEffectValue = rNewEffect.nInit * 3;
					if rNewEffect.sExpire == "start" then
						nNewEffectValue = nNewEffectValue + 1;
					elseif rNewEffect.sExpire == "end" then
						nNewEffectValue = nNewEffectValue - 1;
					end
					
					if ((nEffectValue - nCurrentValue) * (nNewEffectValue - nCurrentValue) < 0) then
						if nNewEffectValue > nEffectValue then
							bLonger = true;
						end
					else
						if nNewEffectValue < nEffectValue then
							bLonger = true;
						end
					end
				end
			end
			
			-- IF LONGER EFFECT, THEN UPDATE EFFECT; OTHERWISE, NOTIFY AND EXIT
			if bLonger then
				nodeTargetEffect = v;
				sMsg = "Effect ['" .. rNewEffect.sName .."'] -> [REPLACED SHORTER] [on " .. sCTName .. "]";
				break;
			end

			message("Effect ['" .. rNewEffect.sName .. "'] -> [ALREADY EXISTS]", nodeCT, false, sUser);
			return;
		end
		
		-- CHECK FOR WEAKER EFFECT
		-- NOTE: ONLY IF MODIFIER AND EXPIRATION ARE THE SAME
		if #aEffectParse == #aNewEffectParse and nEffectSaveMod == rNewEffect.nSaveMod and sEffectExpire == rNewEffect.sExpire then
			-- COMPARE EACH EFFECT CLAUSE
			local bDifferent = false;
			local nDifferentMod = 0;
			for i = 1, #aNewEffectParse do
				-- CHECK TYPE AND REMAINDER FIRST
				if aEffectParse[i].type == aNewEffectParse[i].type then
					
					-- CHECK REMAINDER NEXT
					if #(aEffectParse[i].remainder) == #(aNewEffectParse[i].remainder) then
				    	for j = 1, #(aNewEffectParse[i].remainder) do
				    		if aEffectParse[i].remainder[j] ~= aNewEffectParse[i].remainder[j] then
				    			bDifferent = true;
				    			break;
				    		end
				    	end
					else
				    	bDifferent = true;
					end
				    
				    -- CHECK DICE NEXT
				    if #(aEffectParse[i].dice) == #(aNewEffectParse[i].dice) then
				    	for j = 1, #(aNewEffectParse[i].dice) do
				    		if aEffectParse[i].dice[j] ~= aNewEffectParse[i].dice[j] then
				    			bDifferent = true;
				    			break;
				    		end
				    	end
				    else
				    	bDifferent = true;
				    end
				    
				    -- CHECK MODIFIER NEXT
				    if aEffectParse[i].mod > aNewEffectParse[i].mod then
				    	if nDifferentMod < 0 then
				    		bDifferent = true;
				    	else
				    		nDifferentMod = 1;
				    	end
				    elseif aEffectParse[i].mod < aNewEffectParse[i].mod then
				    	if nDifferentMod > 0 then
				    		bDifferent = true;
				    	else
				    		nDifferentMod = -1;
				    	end
				    end
				end
			
				-- IF DICE DIFFERENT, THEN JUST EXIT
				if bDifferent then
					break;
				end
			end
			
			-- IF DIFFERENCE IS ONLY MODIFIERS AND ALL DIFFERENCES ARE CONSISTENTLY STRONGER OR WEAKER, THEN ACT 
			if not bDifferent and nDifferentMod ~= 0 then
				-- NEW EFFECT IS STRONGER
				if nDifferentMod < 0 then
					nodeTargetEffect = v;
					sMsg = "Effect ['" .. rNewEffect.sName .. "'] -> [REPLACED WEAKER] [on " .. sCTName .. "]";
					break;
				
				-- NEW EFFECT IS WEAKER
				elseif nDifferentMod > 0 then
					message("Effect ['" .. rNewEffect.sName .. "' -> [STRONGER EFFECT EXISTS]", nodeCT, false, sUser);
					return;
				end
			end
		end  -- END DIFFERENCE CHECK
	end  -- END EFFECTS LOOP
	
	-- BLANK EFFECT CHECK
	if not nodeTargetEffect then
		for k, v in pairs(nodeEffectsList.getChildren()) do
			if NodeManager.get(v, "label", "") == "" then
				nodeTargetEffect = v;
				break;
			end
		end
	end
	
	-- CREATE EFFECT, IF ONE NOT PROVIDED
	if not nodeTargetEffect then
		nodeTargetEffect = NodeManager.createChild(nodeEffectsList);
	end
	
	-- ADD EFFECT DETAILS
	NodeManager.set(nodeTargetEffect, "label", "string", rNewEffect.sName);
	NodeManager.set(nodeTargetEffect, "expiration", "string", rNewEffect.sExpire);
	NodeManager.set(nodeTargetEffect, "effectsavemod", "number", rNewEffect.nSaveMod);
	NodeManager.set(nodeTargetEffect, "effectinit", "number", rNewEffect.nInit);
	NodeManager.set(nodeTargetEffect, "isgmonly", "number", rNewEffect.nGMOnly);
	if rNewEffect.sApply then
		NodeManager.set(nodeTargetEffect, "apply", "string", rNewEffect.sApply);
	end

	-- BUILD MESSAGE
	local msg = {font = "msgfont", icon = "indicator_effect"};
	if sMsg ~= "" then
		msg.text = sMsg;
	else
		msg.text = "Effect ['" .. rNewEffect.sName .. "'] -> [to " .. sCTName .. "]";
	end
	
	-- HANDLE APPLIED BY SETTING
	if rNewEffect.sSource and rNewEffect.sSource ~= "" then
		NodeManager.set(nodeTargetEffect, "source_name", "string", rNewEffect.sSource);
		msg.text = msg.text .. " [by " .. NodeManager.get(DB.findNode(rNewEffect.sSource), "name", "") .. "]";
	end
	
	-- HANDLE EFFECT TARGET SETTING
	if sEffectTargetNode and sEffectTargetNode ~= "" then
		TargetingManager.addTarget("host", nodeTargetEffect.getNodeName(), sEffectTargetNode);
	end
	
	-- SEND MESSAGE
	if isGMEffect(nodeCT, nodeTargetEffect) then
		if sUser == "" then
			msg.text = "[GM] " .. msg.text;
			ChatManager.addMessage(msg);
		elseif sUser ~= "" then
			ChatManager.addMessage(msg);
			ChatManager.deliverMessage(msg, sUser);
		end
	else
	  	ChatManager.deliverMessage(msg);
	end
end

-- MAKE SURE AT LEAST ONE EFFECT REMAINS AFTER DELETING
function deleteEffect(nodeEffect)
	local nodeEffectList = nodeEffect.getParent();
	nodeEffect.delete();
	if nodeEffectList.getChildCount() == 0 then
		NodeManager.createChild(nodeEffectList);
	end
end

function expireEffect(nodeActor, nodeEffect, nExpireComponent, bOverride)
	-- VALIDATE
	if not nodeEffect then
		return false;
	end

	-- PARSE THE EFFECT
	local sEffect = NodeManager.get(nodeEffect, "label", "");
	local listEffectComp = parseEffect(sEffect);

	-- DETERMINE MESSAGE VISIBILITY
	local bGMOnly = isGMEffect(nodeActor, nodeEffect);

	-- CHECK FOR PARTIAL EXPIRATION
	if nExpireComponent > 0 then
		if #listEffectComp > 1 then
			table.remove(listEffectComp, nExpireComponent);

			local sNewEffect = rebuildParsedEffect(listEffectComp);
			NodeManager.set(nodeEffect, "label", "string", sNewEffect);

			message("Effect ['" .. sEffect .. "'] -> [SINGLE MOD USED]", nodeActor, bGMOnly);
			return true;
		end
	end
	
	-- CHECK FOR FOLLOW-ON
	local i = 1;
	while i <= #listEffectComp do
		if listEffectComp[i].type == "AFTER" then
			break;
		end
		i = i + 1;
	end
	
	-- IF WE HAVE FOLLOW-ON, THEN STRIP OUT EXPIRED PART OF EFFECT
	local sMsg = "";
	if i <= #listEffectComp and not bOverride then
		sMsg = "Effect ['" .. sEffect .. "'] -> [UPDATED]";

		local sNewExpiration = table.concat(listEffectComp[i].remainder, " ");
		local aNewEffectComp = {};
		for j = i + 1, #listEffectComp do
			table.insert(aNewEffectComp, listEffectComp[j]);
		end
		local sNewEffect = rebuildParsedEffect(aNewEffectComp);
		
		NodeManager.set(nodeEffect, "label", "string", sNewEffect);
		NodeManager.set(nodeEffect, "expiration", "string", sNewExpiration);

	-- IF NO FOLLOW-ON, THEN DELETE THE EFFECT
	else
		sMsg = "Effect ['" .. sEffect .. "'] -> [EXPIRED]";

		deleteEffect(nodeEffect);
	end

	-- SEND NOTIFICATION TO THE HOST
	message(sMsg, nodeActor, bGMOnly);
	return true;
end

function handleFailedSave(nodeActor, nodeEffect)
	-- VALIDATE
	if not nodeEffect then
		return false;
	end

	-- CHECK FOR FOLLOW-ON
	local listEffectComp = parseEffect(NodeManager.get(nodeEffect, "label", ""));
	local i = 1;
	while i <= #listEffectComp do
		if listEffectComp[i].type == "FAIL" then
			break;
		end
		i = i + 1;
	end
	
	-- IF NO FOLLOW-ON, THEN EXIST
	if i > #listEffectComp then
		return false;
	end
	
	-- IF WE HAVE FOLLOW-ON FOR FAILED SAVE, THEN STRIP OUT EXPIRED PART OF EFFECT
	local sExpirationNew = table.concat(listEffectComp[i].remainder, " ");
	local listEffectNew = {};
	for j = i + 1, #listEffectComp do
		local sTemp = "";
		if listEffectComp[j].type ~= "" then
			sTemp = listEffectComp[j].type .. ":";
			if (listEffectComp[j].mod ~= 0) or (#(listEffectComp[j].dice) > 0) then
				sTemp = sTemp .. " " .. StringManager.convertDiceToString(listEffectComp[j].dice, listEffectComp[j].mod);
			end
			sTemp = sTemp .. " " .. table.concat(listEffectComp[j].remainder, " ");
		else
			sTemp = table.concat(listEffectComp[j].remainder, " ");
		end
		table.insert(listEffectNew, sTemp);
	end

	-- BUILD NOTIFICATION MESSAGE
	local sMsg = "Effect ['" .. NodeManager.get(nodeEffect, "label", "") .. "'] -> [UPDATED]";
				
	-- UPDATE THE EFFECT
	NodeManager.set(nodeEffect, "label", "string", table.concat(listEffectNew, "; "));
	NodeManager.set(nodeEffect, "expiration", "string", sExpirationNew);

	-- SEND NOTIFICATION TO THE HOST
	local nodeCTEntry = nodeEffect.getChild("...");
	message(sMsg, nodeCTEntry, isGMEffect(nodeActor, nodeEffect));
	return true;
end

function applyOngoingDamageAdjustment(nodeActor, nodeEffect, rEffectComp)
	-- EXIT IF EMPTY REGEN
	if #(rEffectComp.dice) == 0 and rEffectComp.mod == 0 then
		return;
	end
	
	-- BUILD MESSAGE
	local listResults = {};
	if isGMEffect(nodeActor, nodeEffect) then
		table.insert(listResults, "[GM]");
	end
	if rEffectComp.type == "REGEN" then
		-- MAKE SURE AFFECTED ACTOR IS WOUNDED BEFORE BOTHERING WITH REGENERATION
		if NodeManager.get(nodeActor, "wounds", 0) == 0 then
			return;
		end
		
		table.insert(listResults, "[HEAL] Regeneration");
	else
		table.insert(listResults, "[DAMAGE] Ongoing damage");
	end
	if #(rEffectComp.remainder) > 0 then
		table.insert(listResults, "[TYPE: " .. table.concat(rEffectComp.remainder, ",") .. "]");
	end
	
	-- MAKE ROLL AND APPLY RESULTS
	local sMsg = table.concat(listResults, " ");
	local rTargetActor = CombatCommon.getActor("ct", nodeActor);
	RulesManager.dclkAction("damage", rEffectComp.mod, sMsg, nil, rTargetActor, rEffectComp.dice, true);
end

function applyRecharge(nodeActor, nodeEffect, rEffectComp)
	-- BUILD MESSAGE
	local listResults = {};
	if isGMEffect(nodeActor, nodeEffect) then
		table.insert(listResults, "[GM]");
	end
	table.insert(listResults, "'" .. table.concat(rEffectComp.remainder, " ") .. "' recharges on " .. rEffectComp.mod .. "+");
	
	-- Make the recharge roll
	local sMsg = table.concat(listResults, " ");
	RulesManager.dclkAction("recharge", 0, sMsg, nil, nil, {"d6"}, true, { effect = nodeEffect });
end

function getSaveRollStructures(nodeActor, nodeEffect)
	-- CREATURE
	local rCreature = CombatCommon.getActor("ct", nodeActor);

	-- BUILD SAVE STRUCTURE
	local rSave = {};
	rSave.mod = NodeManager.get(nodeActor, "save", 0);
	if isGMEffect(nodeActor, nodeEffect) then
		rSave.gmflag = true;
	end
	
	-- RESULTS
	return rCreature, rSave;
end

function makeEffectSave(nodeActor, nodeEffect)
	-- DEPENDING ON OPTION, EITHER AUTOMATICALLY MAKE THE SAVING THROW OR JUST NOTIFY
	if OptionsManager.isOption("ESAV", "on") or 
			((OptionsManager.isOption("ESAV", "npc") and NodeManager.get(nodeActor, "type", "") ~= "pc")) then
		local rActor, rSave = getSaveRollStructures(nodeActor, nodeEffect);
		local save_name, save_dice, save_mod = RulesManager.buildSaveRoll(rActor, rSave, -1);
		RulesManager.dclkAction("autosave", save_mod, save_name, rActor, nil, save_dice, false, {effect = nodeEffect});
	else
		local sMsg = "Effect ['" .. NodeManager.get(nodeEffect, "label", "") .. 
				"'] -> [ALLOWS SAVE]";
		
		message(sMsg, nodeEffect.getChild("..."), isGMEffect(nodeActor, nodeEffect));
	end
end

function processEffects(nodeActorList, nodeCurrentActor, nodeNewActor)
	-- SETUP CURRENT AND NEW INITIATIVE VALUES
	local nCurrentInit = 10000;
	if nodeCurrentActor then
		nCurrentInit = NodeManager.get(nodeCurrentActor, "initresult", 0); 
	end
	local nNewInit = -10000;
	if nodeNewActor then
		nNewInit = NodeManager.get(nodeNewActor, "initresult", 0);
	end
	
	-- ITERATE THROUGH EACH ACTOR
	for keyActor, nodeActor in pairs(nodeActorList.getChildren()) do
		-- ITERATE THROUGH EACH EFFECT
		local nodeEffectList = nodeActor.getChild("effects");
		for keyEffect, nodeEffect in pairs(nodeEffectList.getChildren()) do
			-- MAKE SURE THE EFFECT IS ACTIVE
			if NodeManager.get(nodeEffect, "isactive", 0) == 1 then
			
				-- GET EFFECT DETAILS
				local sEffName = NodeManager.get(nodeEffect, "label", "");
				local sEffExp = NodeManager.get(nodeEffect, "expiration", "");
				local nEffInit = NodeManager.get(nodeEffect, "effectinit", "");
				
				-- HANDLE END OF TURN EFFECTS FOR CURRENT ACTOR
				local flagEndEffect = false;
				if sEffExp == "end" then
					if nEffInit <= nCurrentInit and nEffInit > nNewInit then
						expireEffect(nodeActor, nodeEffect, 0);
					end
				elseif sEffExp == "endnext" then
					if nEffInit <= nCurrentInit and nEffInit > nNewInit then
						NodeManager.set(nodeEffect, "expiration", "string", "end");
					end
				elseif sEffExp == "save" and nodeActor == nodeCurrentActor then
					makeEffectSave(nodeActor, nodeEffect);
				end
					
				-- HANDLE START OF TURN EFFECTS
				if sEffExp == "start" then
					if nEffInit < nCurrentInit and nEffInit >= nNewInit then
						expireEffect(nodeActor, nodeEffect, 0);
					end
				end
				
				-- HANDLE TRANSITION TO NEW ACTOR
				if nodeActor == nodeNewActor then
					local listEffectComp = parseEffect(sEffName);
					for keyEffectComp, rEffectComp in ipairs(listEffectComp) do
						-- CHECK FOR EFFECTS THE BREAK EXECUTION
						if rEffectComp.type == "AFTER" or rEffectComp.type == "FAIL" then
							break;
						end
						
						-- ONGOING DAMAGE ADJUSTMENT (INCLUDING REGENERATION)
						if rEffectComp.type == "DMGO" or rEffectComp.type == "REGEN" then
							applyOngoingDamageAdjustment(nodeActor, nodeEffect, rEffectComp);
						end

						-- NPC POWER RECHARGE
						if rEffectComp.type == "RCHG" then
							applyRecharge(nodeActor, nodeEffect, rEffectComp);
						end
					end
				end
			end -- END ACTIVE EFFECT CHECK
		end -- END EFFECT LOOP
	end -- END ACTOR LOOP
end

function evalEffect(rActor, s)
	if PremiumEffectsManager then
		return PremiumEffectsManager.evalEffect(rActor, s);
	end
	
	return s;
end

function getEffectsByType(node_ctentry, effecttype, aFilter, rFilterActor, bTargetedOnly)
	if PremiumEffectsManager then
		return PremiumEffectsManager.getEffectsByType(node_ctentry, effecttype, aFilter, rFilterActor, bTargetedOnly);
	end

	return {};
end

function getEffectsBonusByType(node_ctentry, effecttype, flag_addemptybonus, aFilter, rFilterActor, bTargetedOnly)
	if PremiumEffectsManager then
		return PremiumEffectsManager.getEffectsBonusByType(node_ctentry, effecttype, flag_addemptybonus, aFilter, rFilterActor, bTargetedOnly);
	end

	return {}, 0;
end

function getEffectsBonus(node_ctentry, effecttype, modonly, aFilter, rFilterActor, bTargetedOnly)
	if PremiumEffectsManager then
		return PremiumEffectsManager.getEffectsBonus(node_ctentry, effecttype, modonly, aFilter, rFilterActor, bTargetedOnly);
	end
	
	if modonly then
		return 0, 0;
	end

	return {}, 0, 0;
end

function hasEffectCondition(nodeSourceActor, sEffect)
	if PremiumEffectsManager then
		return PremiumEffectsManager.hasEffectCondition(nodeSourceActor, sEffect);
	end
	
	return nil;
end

function hasEffect(nodeSourceActor, sEffect, nodeTargetActor, bTargetedOnly, bIgnoreEffectTargets)
	if PremiumEffectsManager then
		return PremiumEffectsManager.hasEffect(nodeSourceActor, sEffect, nodeTargetActor, bTargetedOnly, bIgnoreEffectTargets);
	end
	
	return nil;
end

